package com.java.springcloud.aop;

import com.google.gson.Gson;
import com.java.springcloud.annotation.AutoCache;
import com.java.springcloud.util.RedisTemplateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @Author zhangtao
 * @create 2022/5/19 16:17
 */
@Aspect
@Component
@Slf4j
public class CacheIntercepter {

    private final Gson gson = new Gson();

    @Autowired
    private RedisTemplateUtils resultCache;

    @Around("@annotation( com.java.springcloud.annotation.AutoCache)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        AutoCache methodCache = getAnnotation(joinPoint, AutoCache.class);
        String cacheKey = getCacheKey(joinPoint, methodCache);

        //刷新只做缓存更新
        if (methodCache.refresh()) {
            Object result = joinPoint.proceed(joinPoint.getArgs());
            if (result == null) {
                log.error("方法的结果为空，key [{}]", cacheKey);
            } else {
                resultCache.set(cacheKey, gson.toJson(result), methodCache.expire());
            }
            return result;
        }

        String obj = resultCache.get(cacheKey);
        if (obj != null) {
            log.info("自动从缓存获取结果，key [{}]", cacheKey);
            return gson.fromJson(obj, joinPoint.getTarget().getClass());
        } else {
            log.debug("没有命中缓存，key [{}]", cacheKey);
            Object result = joinPoint.proceed(joinPoint.getArgs());
            if (result == null) {
                log.error("方法的结果为空，key [{}]", cacheKey);
            } else {
                resultCache.set(cacheKey, gson.toJson(result), methodCache.expire());
            }
            return result;
        }
    }

    private String getCacheKey(ProceedingJoinPoint joinPoint, AutoCache cache) {
        if (StringUtils.isBlank(cache.key())) {
            return String.format("{}-{}.{}",
                    cache.name(),
                    joinPoint.getSignature().toString().split("\\s")[1],
                    StringUtils.join(joinPoint.getArgs(), ",")
            );
        } else {
            EvaluationContext context = new StandardEvaluationContext();
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(cache.key());
            Object[] paramValues = joinPoint.getArgs();
            String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], paramValues[i]);
            }
            Object n = expression.getValue(context);
            return String.format("{}-{}", cache.name(), n);
        }
    }

    private <T extends Annotation> T getAnnotation(ProceedingJoinPoint jp, Class<T> clazz) {
        MethodSignature sign = (MethodSignature) jp.getSignature();
        Method method = sign.getMethod();
        return method.getAnnotation(clazz);
    }

}
